package com.zzb.framework.query.sqlbuilder;

import com.baomidou.mybatisplus.annotation.TableField;
import com.google.common.collect.Lists;
import com.zzb.framework.common.PageSearchForm;
import com.zzb.framework.query.FlexiblePageSearchForm;
import com.zzb.framework.query.IPageSearchForm;
import com.zzb.framework.query.QueryMethodEnum;
import com.zzb.framework.query.QueryMethodItem;
import com.zzb.framework.utils.DateUtil;
import com.zzb.framework.utils.ReflectionUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;

/**
 * 创建搜索方法的美剧
 * Created by zhangzhenbin on 2016/12/29.
 */
public class QueryMethodBuilder {
    private static final Logger logger = LoggerFactory.getLogger(QueryMethodBuilder.class);

    public static <T extends IPageSearchForm> List<QueryMethodItem> build(T searchForm, Class tableClazz)
            throws IllegalAccessException {
        List<QueryMethodItem> list = new ArrayList<>();
        if (searchForm instanceof FlexiblePageSearchForm) {
            // 自动构建条件
            FlexiblePageSearchForm flexiblePageSearchForm = (FlexiblePageSearchForm) searchForm;
            Set<String> keySet = flexiblePageSearchForm.keySet();
            for (String searchFormFieldName : keySet) {
                if (searchFormFieldName.equals("page")
                        || searchFormFieldName.equals("pageSize")
                        || searchFormFieldName.equals("orderBy")
                        || searchFormFieldName.equals("additionalCondition")) continue;
                QueryMethodEnum[] enums = QueryMethodEnum.values();
                for (QueryMethodEnum anEnum : enums) {
                    if (searchFormFieldName.startsWith(anEnum.name() + "_")) {
                        QueryMethodItem queryMethodItem = new QueryMethodItem();
                        queryMethodItem.setMethod(anEnum);
                        String fieldName = searchFormFieldName.replace(anEnum.name() + "_", "");
                        if (!anEnum.equals(QueryMethodEnum.in) && !anEnum.equals(QueryMethodEnum.notin)) {
                            queryMethodItem.setValue(flexiblePageSearchForm.get(searchFormFieldName));
                        } else {
                            Object o = flexiblePageSearchForm.get(searchFormFieldName);
                            if (buildInConditionQueryValue(searchFormFieldName, queryMethodItem, o)) continue;
                        }
                        if (obtainColumnName(tableClazz, queryMethodItem, fieldName)) continue;

                        list.add(queryMethodItem);
                    }
                }
            }
        } else {
            // 根据定义字段构建条件
            Class<? extends IPageSearchForm> searchFormClazz = searchForm.getClass();

            Field[] searchFormFields = ReflectionUtils.getAllDeclaredFields(searchFormClazz);
            for (Field searchFormField : searchFormFields) {
                //如果是pageSize，curPageNO，pageProvider字段，则直接忽略
                if (searchFormField.getName().equals("page")
                        || searchFormField.getName().equals("pageSize")
                        || searchFormField.getName().equals("additionalCondition")
                        || searchFormField.getName().equals("orderBy")) continue;
                //如果是有Transient注解，也直接忽略
                if ((searchFormField.getModifiers() & Modifier.TRANSIENT) != 0) continue;

                String searchFormFieldName = searchFormField.getName();
                QueryMethodEnum[] enums = QueryMethodEnum.values();
                for (QueryMethodEnum anEnum : enums) {
                    if (searchFormFieldName.startsWith(anEnum.name() + "_")) {
                        QueryMethodItem queryMethodItem = new QueryMethodItem();
                        queryMethodItem.setMethod(anEnum);
                        String fieldName = searchFormFieldName.replace(anEnum.name() + "_", "");
                        if (!anEnum.equals(QueryMethodEnum.in) && !anEnum.equals(QueryMethodEnum.notin)) {
                            queryMethodItem.setValue(FieldUtils.readDeclaredField(searchForm, searchFormFieldName, true));
                        } else {
                            try {
                                Object o = FieldUtils.readDeclaredField(searchForm, searchFormFieldName, true);
                                if (buildInConditionQueryValue(searchFormFieldName, queryMethodItem, o)) continue;
                            } catch (IllegalAccessException e) {
                                logger.error("搜索条件[" + searchFormFieldName + "]构建失败", e);
                                continue;
                            }
                        }
                        if (obtainColumnName(tableClazz, queryMethodItem, fieldName)) continue;

                        list.add(queryMethodItem);
                    }
                }

            }
        }
        return list;
    }

    /**
     * 构建 in 查询条件的查询值
     *
     * @param searchFormFieldName
     * @param queryMethodItem
     * @param o
     * @return
     */
    private static boolean buildInConditionQueryValue(String searchFormFieldName, QueryMethodItem queryMethodItem, Object o) {
        if (o instanceof String) {
            String s = (String) o;
            String[] strings = s.split(",");
            queryMethodItem.setValues(Lists.newArrayList(strings));
        } else if (o instanceof Collection) {
            queryMethodItem.setValues((List) o);
        } else if (o instanceof Object[]) {
            Object[] objects = (Object[]) o;
            queryMethodItem.setValues(Lists.newArrayList(objects));
        } else {
            logger.error("搜索条件[" + searchFormFieldName + "]构建失败,in搜索条件的类型必须是String或Collection或Object[]");
            return true;
        }
        return false;
    }

    /**
     * 获取查询条件的字段名称
     *
     * @param tableClazz
     * @param queryMethodItem
     * @param fieldName
     * @return
     */
    private static boolean obtainColumnName(Class tableClazz, QueryMethodItem queryMethodItem, String fieldName) {
        try {
            Field declaredField = ReflectionUtils.getAllDeclaredField(tableClazz, fieldName);
            queryMethodItem.setType(declaredField.getType());
            TableField columnAnnotation = declaredField.getAnnotation(TableField.class);
            if (columnAnnotation != null) {
                String columnName = columnAnnotation.value();
                queryMethodItem.setColumnName(columnName);
            } else {
                queryMethodItem.setColumnName(declaredField.getName());
            }

        } catch (Exception e) {
            logger.debug("没有在[" + tableClazz.getName() + "]中找到[" + fieldName + "]字段");
            return true;
        }
        return false;
    }

    /**
     * 将 methodItemList转换为SQL 和 参数
     *
     * @param methodItemList
     * @param sqlCondition
     * @param params
     */
    public static void methodItemListBuilder(List<QueryMethodItem> methodItemList, StringBuilder sqlCondition, List<Object> params) {
        for (QueryMethodItem item : methodItemList) {
            //包含空值就取消
            if (item.getMethod() == null ||
                    StringUtils.isBlank(item.getColumnName()) ||
                    item.getType() == null ||
                    (item.getValue() == null && (!item.getMethod().equals(QueryMethodEnum.in) && !item.getMethod().equals(QueryMethodEnum.notin))) ||
                    (CollectionUtils.isEmpty(item.getValues()) && (item.getMethod().equals(QueryMethodEnum.in) || item.getMethod().equals(QueryMethodEnum.notin))) ||
                    (item.getValue() instanceof String && StringUtils.isBlank((String) item.getValue()))
            ) continue;
            if (!item.getMethod().equals(QueryMethodEnum.in) && !item.getMethod().equals(QueryMethodEnum.notin)) {
                sqlCondition
                        .append("and ")
                        .append("t.")
                        .append(item.getColumnName())
                        .append(" ")
                        .append(item.getMethod().getValue())
                        .append(" ? ");
                Object value = item.getValue();
                if (item.getMethod().equals(QueryMethodEnum.contain)) {
                    value = "%" + value + "%";
                }
                if (item.getType().equals(Date.class) && value instanceof String) {
                    value = DateUtil.stringForceToDate((String) value);
                }
                params.add(value);
            } else {
                sqlCondition
                        .append("and ")
                        .append("t.")
                        .append(item.getColumnName())
                        .append(" ")
                        .append(item.getMethod().getValue())
                        .append("(");
                for (Object o : item.getValues()) {
                    sqlCondition.append(" ? ,");
                    params.add(o);
                }
                sqlCondition.deleteCharAt(sqlCondition.length() - 1);
                sqlCondition.append(" ) ");
            }
        }
    }
}
